package com.ruoyi.framework.datafill;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.framework.mybatisplus.PageDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
public class DataFillActuator {

    @Resource
    private FillThreadPoolManager threadPoolManager;

    public void execute(Object o){
        List<Metadata> metaDataList = fetchMetaData(o);
        if(!metaDataList.isEmpty()){
            Object r = fillData(metaDataList);
            execute(r);
        }
    }

    private Object fillData(List<Metadata> metaDataList) {


        Map<FillSource, List<Metadata>> sourceMetadataMap = metaDataList.stream()
                .collect(Collectors.groupingBy(Metadata::getSource));


        List<FillTask> fillTasks = new ArrayList<>();

        for (Map.Entry<FillSource, List<Metadata>> entry : sourceMetadataMap.entrySet()) {
            Optional<FillFieldStrategy> strategy = FillFieldStrategyContext.getStrategy(entry.getKey().value());

            if(strategy.isEmpty()){
                log.error("不支持的数据类型:{}, 找不到匹配的填充策略", entry.getKey());
                continue;
            }

            fillTasks.add(new FillTask(strategy.get(), entry.getKey().value(), entry.getKey().sourceEntity()
                    , entry.getKey().sourceKey(), entry.getValue()));

        }


        threadPoolManager.awaitSubmit(fillTasks);


        List<Object> r =  new ArrayList<>();

        for(Metadata metadata : metaDataList){
            Object fieldValue = ReflectUtil.getFieldValue(metadata.getTarget(), metadata.getTargetField());

            // 值不为空的情况下跳过
            if(fieldValue == null){
                continue;
            }

            // 基本数据类型跳过
            if (ClassUtil.isBasicType(metadata.getTargetField().getType())
                    || metadata.getTargetField().getType().isAssignableFrom(String.class)) {
                continue;
            }

            // 组装返回值以供下一个级别拉取数据
            if(ClassUtil.isAssignable(Collection.class, metadata.getTargetField().getType())){
                r.addAll((Collection<?>) fieldValue);
            }else{
                r.add(fieldValue);
            }
        }

        return r;
    }


    private List<Metadata> fetchMetaData(Object o) {

        Collection<Object> list = unpack(o);

        if(list.isEmpty()){
            return Collections.emptyList();
        }

        List<Metadata> metaDataList = new ArrayList<>();

        for (Object object : list) {
            Field[] fields = ReflectUtil.getFields(object.getClass());

            for (Field field : fields) {
                FillField fillField = field.getAnnotation(FillField.class);

                if(fillField == null){
                    continue;
                }

                FillType fillType;

                if (ClassUtil.isBasicType(field.getType())
                        || field.getType().isAssignableFrom(String.class)) {
                    // 基本数据类型
                    fillType = FillType.PropertyFill;
                }else{
                    fillType = FillType.ObjectFill;
                }


                Object fieldValue = ReflectUtil.getFieldValue(object, field);

                if(fillType == FillType.ObjectFill && fieldValue == null){
                    log.error("FillType.ObjectFill object field value is null {}", field);
                    continue;
                }

                Metadata metadata = new Metadata();
                metadata.setTarget(object);
                metadata.setTargetField(field);
                metadata.setQueryPk(fillField.queryPk());
                metadata.setSource(fillField.source());
                metadata.setType(fillType);
                metaDataList.add(metadata);
            }
        }

        return metaDataList;
    }


    /**
     * 数据解包
     * @param o
     * @return
     */
    private Collection<Object> unpack(Object o){

        if (o instanceof Collection) {
            return (Collection<Object>) o;
        } else if (o instanceof PageDTO<?> pageDto) {
            return (Collection<Object>) pageDto.getRecords();
        }else if(o instanceof R<?>){
            R r = (R) o;
            return unpack(r.getData());
        }else{
            return Collections.singletonList(o);
        }
    }

}
